home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / edit / elv18src.zip / ctags.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-17  |  20.3 KB  |  908 lines

  1. /* ctags.c */
  2.  
  3. /* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
  4.  * and has heaps o' flags.  It is meant to be distributed with elvis.
  5.  */
  6.  
  7. #include <stdio.h>
  8. #ifdef __STDC__
  9. # include <string.h>
  10. # include <stdlib.h>
  11. #endif
  12. #include "config.h"
  13. #ifndef FALSE
  14. # define FALSE    0
  15. # define TRUE    1
  16. #endif
  17. #ifndef TAGS
  18. # define TAGS    "tags"
  19. #endif
  20. #ifndef REFS
  21. # define REFS    "refs"
  22. #endif
  23. #ifndef BLKSIZE
  24. # define BLKSIZE 1024
  25. #endif
  26.  
  27. #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
  28.  
  29. extern void    file_open P_((char *));
  30. extern int    file_getc P_((void));
  31. extern void    file_ungetc P_((int));
  32. extern void    file_copyline P_((long, FILE *));
  33. extern void    cpp_open P_((char *));
  34. extern void    cpp_echo P_((int));
  35. extern int    cpp_getc P_((void));
  36. extern void    cpp_ungetc P_((int));
  37. extern int    lex_gettoken P_((void));
  38. extern void    maketag P_((int, long));
  39. extern void    ctags P_((char *));
  40. extern void    usage P_((void));
  41. extern void    main P_((int, char **));
  42.  
  43.  
  44. /* -------------------------------------------------------------------------- */
  45. /* Some global variables */
  46.  
  47. /* The following boolean variables are set according to command line flags */
  48. int    backward;    /* -B  regexp patterns search backwards */
  49. int    no_colons;    /* -S  make static tags look like global tags */
  50. int    incl_static;    /* -s  include static tags */
  51. int    incl_types;    /* -t  include typedefs and structs */
  52. int    incl_vars;    /* -v  include variables */
  53. int    make_refs;    /* -r  generate a "refs" file */
  54. int    append_files;    /* -a  append to "tags" [and "refs"] files */
  55. #ifdef DEVNULL
  56. int    dont_make_tags;    /* -T  Don't make \"tags\" */
  57. #endif
  58.  
  59. /* The following are used for outputting to the "tags" and "refs" files */
  60. FILE    *tags;        /* used for writing to the "tags" file */
  61. FILE    *refs;        /* used for writing to the "refs" file */
  62.  
  63. /* -------------------------------------------------------------------------- */
  64. /* These are used for reading a source file.  It keeps track of line numbers */
  65. char    *file_name;    /* name of the current file */
  66. FILE    *file_fp;    /* stream used for reading the file */
  67. long    file_lnum;    /* line number in the current file */
  68. long    file_seek;    /* fseek() offset to the start of current line */
  69. int    file_afternl;    /* boolean: was previous character a newline? */
  70. int    file_prevch;    /* a single character that was ungotten */
  71. int    file_header;    /* boolean: is the current file a header file? */
  72.  
  73. /* This function opens a file, and resets the line counter.  If it fails, it
  74.  * it will display an error message and leave the file_fp set to NULL.
  75.  */
  76. void file_open(name)
  77.     char    *name;    /* name of file to be opened */
  78. {
  79.     /* if another file was already open, then close it */
  80.     if (file_fp)
  81.     {
  82.         fclose(file_fp);
  83.     }
  84.  
  85.     /* try to open the file for reading.  The file must be opened in
  86.      * "binary" mode because otherwise fseek() would misbehave under DOS.
  87.      */
  88. #if MSDOS || TOS || OS2
  89.     file_fp = fopen(name, "rb");
  90. #else
  91.     file_fp = fopen(name, "r");
  92. #endif
  93.     if (!file_fp)
  94.     {
  95.         perror(name);
  96.     }
  97.  
  98.     /* reset the name & line number */
  99.     file_name = name;
  100.     file_lnum = 0L;
  101.     file_seek = 0L;
  102.     file_afternl = TRUE;
  103.  
  104.     /* determine whether this is a header file */
  105.     file_header = FALSE;
  106.     name += strlen(name) - 2;
  107.     if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
  108.     {
  109.         file_header = TRUE;
  110.     }
  111. }
  112.  
  113. /* This function reads a single character from the stream.  If the *previous*
  114.  * character was a newline, then it also increments file_lnum and sets
  115.  * file_offset.
  116.  */
  117. int file_getc()
  118. {
  119.     int    ch;
  120.  
  121.     /* if there is an ungotten character, then return it.  Don't do any
  122.      * other processing on it, though, because we already did that the
  123.      * first time it was read.
  124.      */
  125.     if (file_prevch)
  126.     {
  127.         ch = file_prevch;
  128.         file_prevch = 0;
  129.         return ch;
  130.     }
  131.  
  132.     /* if previous character was a newline, then we're starting a line */
  133.     if (file_afternl && file_fp)
  134.     {
  135.         file_afternl = FALSE;
  136.         file_seek = ftell(file_fp);
  137.         file_lnum++;
  138.     }
  139.  
  140.     /* Get a character.  If no file is open, then return EOF */
  141.     ch = (file_fp ? getc(file_fp) : EOF);
  142.  
  143.     /* if it is a newline, then remember that fact */
  144.     if (ch == '\n')
  145.     {
  146.         file_afternl = TRUE;
  147.     }
  148.  
  149.     /* return the character */
  150.     return ch;
  151. }
  152.  
  153. /* This function ungets a character from the current source file */
  154. void file_ungetc(ch)
  155.     int    ch;    /* character to be ungotten */
  156. {
  157.     file_prevch = ch;
  158. }
  159.  
  160. /* This function copies the current line out some other fp.  It has no effect
  161.  * on the file_getc() function.  During copying, any '\' characters are doubled
  162.  * and a leading '^' or trailing '$' is also quoted.  The '\n' character is not
  163.  * copied.  If the '\n' is preceded by a '\r', then the '\r' isn't copied.
  164.  *
  165.  * This is meant to be used when generating a tag line.
  166.  */
  167. void file_copyline(seek, fp)
  168.     long    seek;    /* where the lines starts in the source file */
  169.     FILE    *fp;    /* the output stream to copy it to */
  170. {
  171.     long    oldseek;/* where the file's pointer was before we messed it up */
  172.     char    ch;    /* a single character from the file */
  173.     char    next;    /* the next character from this file */
  174.  
  175.     /* go to the start of the line */
  176.     oldseek = ftell(file_fp);
  177.     fseek(file_fp, seek, 0);
  178.  
  179.     /* if first character is '^', then quote it */
  180.     ch = getc(file_fp);
  181. #if 0
  182.     if (ch == '^')
  183.     {
  184.         putc('\\', fp);
  185.     }
  186. #endif
  187.  
  188.     /* write everything up to, but not including, the newline */
  189.     while (ch != '\n')
  190.     {
  191.         /* preread the next character from this file */
  192.         next = getc(file_fp);
  193.  
  194.         /* if character is '\', or a terminal '$', then quote it */
  195.         if (ch == '\\'
  196.          || ch == (backward ? '?' : '/')
  197.          || (ch == '$' && next == '\n'))
  198.         {
  199.             putc('\\', fp);
  200.         }
  201.  
  202.         /* copy the character, unless it is a terminal '\r' */
  203.         if (ch != '\r' || next != '\n')
  204.             putc(ch, fp);
  205.  
  206.         /* next character... */
  207.         ch = next;
  208.     }
  209.  
  210.     /* seek back to the old position */
  211.     fseek(file_fp, oldseek, 0);
  212. }
  213.  
  214. /* -------------------------------------------------------------------------- */
  215. /* This section handles preprocessor directives.  It strips out all of the
  216.  * directives, and may emit a tag for #define directives.
  217.  */
  218.  
  219. int    cpp_afternl;    /* boolean: look for '#' character? */
  220. int    cpp_prevch;    /* an ungotten character, if any */
  221. int    cpp_refsok;    /* boolean: can we echo characters out to "refs"? */
  222. int    cpp_refsnl;    /* boolean: dup next \n in "refs"? */
  223.  
  224. /* This function opens the file & resets variables */
  225. void cpp_open(name)
  226.     char    *name;    /* name of source file to be opened */
  227. {
  228.     /* use the lower-level file_open function to open the file */
  229.     file_open(name);
  230.  
  231.     /* reset variables */
  232.     cpp_afternl = TRUE;
  233.     cpp_refsok = TRUE;
  234. }
  235.  
  236. /* This function copies a character from the source file to the "refs" file */
  237. void cpp_echo(ch)
  238.     int    ch; /* the character to copy */
  239. {
  240.     static    wasnl;
  241.  
  242.     /* echo non-EOF chars, unless not making "ref", or echo turned off */
  243.     if (ch != EOF && make_refs && cpp_refsok && !file_header)
  244.     {
  245.         /* try to avoid blank lines */
  246.         if (ch == '\n')
  247.         {
  248.             /* hack: double \n at end of declarations, to
  249.                help `ref' find the right starting point...
  250.             */
  251.             if (cpp_refsnl)
  252.             {
  253.                 putc('\n', refs);
  254.                 cpp_refsnl = FALSE;
  255.             }
  256.             if (wasnl)
  257.             {
  258.                 return;
  259.             }
  260.             wasnl = TRUE;
  261.         }
  262.         else
  263.         {
  264.             wasnl = FALSE;
  265.         }
  266.  
  267.         /* add the character */
  268.         putc(ch, refs);
  269.     }
  270. }
  271.  
  272. /* This function returns the next character which isn't part of a directive */
  273. int cpp_getc()
  274. {
  275.     static
  276.     int    ch;    /* the next input character */
  277.     char    *scan;
  278.  
  279.     /* if we have an ungotten character, then return it */
  280.     if (cpp_prevch)
  281.     {
  282.         ch = cpp_prevch;
  283.         cpp_prevch = 0;
  284.         return ch;
  285.     }
  286.  
  287.     /* Get a character from the file.  Return it if not special '#' */
  288.     ch = file_getc();
  289.     if (ch == '\n')
  290.     {
  291.         cpp_afternl = TRUE;
  292.         cpp_echo(ch);
  293.         return ch;
  294.     }
  295.     else if (ch != '#' || !cpp_afternl)
  296.     {
  297.         /* normal character.  Any non-whitespace should turn off afternl */
  298.         if (ch != ' ' && ch != '\t')
  299.         {
  300.             cpp_afternl = FALSE;
  301.         }
  302.         cpp_echo(ch);
  303.         return ch;
  304.     }
  305.  
  306.     /* Yikes!  We found a directive */
  307.  
  308.     /* see whether this is a #define line */
  309.     scan = " define ";
  310.     while (*scan)
  311.     {
  312.         if (*scan == ' ')
  313.         {
  314.             /* space character matches any whitespace */
  315.             do
  316.             {
  317.                 ch = file_getc();
  318.             } while (ch == ' ' || ch == '\t');
  319.             file_ungetc(ch);
  320.         }
  321.         else
  322.         {
  323.             /* other characters should match exactly */
  324.             ch = file_getc();
  325.             if (ch != *scan)
  326.             {
  327.                 file_ungetc(ch);
  328.                 break;
  329.             }
  330.         }
  331.         scan++;
  332.     }
  333.  
  334.     /* is this a #define line?  and should we generate a tag for it? */
  335.     if (!*scan && (file_header || incl_static))
  336.     {
  337.         /* if not a header, then this will be a static tag */
  338.         if (!file_header && !no_colons)
  339.         {
  340.             fputs(file_name, tags);
  341.             putc(':', tags);
  342.         }
  343.  
  344.         /* output the tag name */
  345.         for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
  346.         {
  347.             putc(ch, tags);
  348.         }
  349.  
  350.         /* output a tab, the filename, another tab, and the line number */
  351.         fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
  352.     }
  353.  
  354.     /* skip to the end of the directive -- a newline that isn't preceded
  355.      * by a '\' character.
  356.      */
  357.     while (ch != EOF && ch != '\n')
  358.     {
  359.         if (ch == '\\')
  360.         {
  361.             ch = file_getc();
  362.         }
  363.         ch = file_getc();
  364.     }
  365.  
  366.     /* return the newline that we found at the end of the directive */
  367.     cpp_echo(ch);
  368.     return ch;
  369. }
  370.  
  371. void
  372. /* This puts a character back into the input queue for the source file */
  373. cpp_ungetc(ch)
  374.     int    ch;    /* a character to be ungotten */
  375. {
  376.     cpp_prevch = ch;
  377. }
  378.  
  379.  
  380. /* -------------------------------------------------------------------------- */
  381. /* This is the lexical analyser.  It gets characters from the preprocessor,
  382.  * and gives tokens to the parser.  Some special codes are...
  383.  *   (deleted)  / *...* / (comments)
  384.  *   (deleted)    //...\n    (comments)
  385.  *   (deleted)    (*    (parens used in complex declaration)
  386.  *   (deleted)    [...]    (array subscript, when ... contains no ])
  387.  *   (deleted)    struct    (intro to structure declaration)
  388.  *   BODY    {...}    ('{' can occur anywhere, '}' only at BOW if ... has '{')
  389.  *   ARGS    (...{    (args of function, not extern or forward)
  390.  *   ARGS    (...);    (args of an extern/forward function declaration)
  391.  *   COMMA    ,    (separate declarations that have same scope)
  392.  *   SEMICOLON    ;    (separate declarations that have different scope)
  393.  *   SEMICOLON  =...;    (initializer)
  394.  *   TYPEDEF    typedef    (the "typedef" keyword)
  395.  *   KSTATIC    static    (the "static" keyword)
  396.  *   KSTATIC    private    (the "static" keyword)
  397.  *   KSTATIC    PRIVATE    (the "static" keyword)
  398.  *   NAME    [a-z]+    (really any valid name that isn't reserved word)
  399.  */
  400.  
  401. /* #define EOF -1 */
  402. #define DELETED      0
  403. #define BODY      1
  404. #define ARGS      2
  405. #define COMMA      3
  406. #define SEMICOLON 4
  407. #define TYPEDEF   5
  408. #define KSTATIC      6
  409. #define EXTERN      7
  410. #define NAME      8
  411.  
  412. char    lex_name[BLKSIZE];    /* the name of a "NAME" token */
  413. long    lex_seek;        /* start of line that contains lex_name */
  414.  
  415. int
  416. lex_gettoken()
  417. {
  418.     int    ch;        /* a character from the preprocessor */
  419.     int    next;        /* the next character */
  420.     int    token;        /* the token that we'll return */
  421.     int    i;
  422.  
  423.     /* loop until we get a token that isn't "DELETED" */
  424.     do
  425.     {
  426.         /* get the next character */
  427.         ch = cpp_getc();
  428.  
  429.         /* process the character */
  430.         switch (ch)
  431.         {
  432.           case ',':
  433.             token = COMMA;
  434.             break;
  435.  
  436.           case ';':
  437.             token = SEMICOLON;
  438.             break;
  439.  
  440.           case '/':
  441.             /* get the next character */
  442.             ch = cpp_getc();
  443.             switch (ch)
  444.             {
  445.               case '*':    /* start of C comment */
  446.                 ch = cpp_getc();
  447.                 next = cpp_getc();
  448.                 while (next != EOF && (ch != '*' || next != '/'))
  449.                 {
  450.                     ch = next;
  451.                     next = cpp_getc();
  452.                 }
  453.                 break;
  454.  
  455.               case '/':    /* start of a C++ comment */
  456.                 do
  457.                 {
  458.                     ch = cpp_getc();
  459.                 } while (ch != '\n' && ch != EOF);
  460.                 break;
  461.  
  462.               default:    /* some other slash */
  463.                 cpp_ungetc(ch);
  464.             }
  465.             token = DELETED;
  466.             break;
  467.  
  468.           case '(':
  469.             ch = cpp_getc();
  470.             if (ch == '*')
  471.             {
  472.                 token = DELETED;
  473.             }
  474.             else
  475.             {
  476.                 next = cpp_getc();
  477.                 while (ch != '{' && ch != EOF            /* }(  to keep vi's % command happy */
  478.                     && (ch != ')' && ch != ']'
  479.                     || next != ';' && next != ','))
  480.                 {
  481.                     ch = next;
  482.                     next = cpp_getc();
  483.                 }
  484.                 if (ch == '{')/*}*/
  485.                 {
  486.                     cpp_ungetc(ch);
  487.                 }
  488.                 else if (next == ';' || next == ',')
  489.                 {
  490.                     cpp_ungetc(next);
  491.                 }
  492.                 token = ARGS;
  493.             }
  494.             break;
  495.  
  496.           case '{':/*}*/
  497.             /* don't send the next characters to "refs" */
  498.             cpp_refsok = FALSE;
  499.  
  500.             /* skip ahead to closing '}', or to embedded '{' */
  501.             do
  502.             {
  503.                 ch = cpp_getc();
  504.             } while (ch != '{' && ch != '}' && ch != EOF);
  505.  
  506.             /* if has embedded '{', then skip to '}' in column 1 */
  507.             if (ch == '{') /*}*/
  508.             {
  509.                 ch = cpp_getc();
  510.                 next = cpp_getc();
  511.                 while (ch != EOF && (ch != '\n' || next != '}'))/*{*/
  512.                 {
  513.                     ch = next;
  514.                     next = cpp_getc();
  515.                 }
  516.             }
  517.  
  518.             /* resume "refs" processing */
  519.             cpp_refsok = TRUE;
  520.             cpp_echo('}');
  521.  
  522.             token = BODY;
  523.             cpp_refsnl = TRUE;
  524.             break;
  525.  
  526.           case '[':
  527.             /* skip to matching ']' */
  528.             do
  529.             {
  530.                 ch = cpp_getc();
  531.             } while (ch != ']' && ch != EOF);
  532.             token = DELETED;
  533.             break;
  534.  
  535.           case '=':
  536.               /* skip to next ';' */
  537.             do
  538.             {
  539.                 ch = cpp_getc();
  540.  
  541.                 /* leave array initializers out of "refs" */
  542.                 if (ch == '{')
  543.                 {
  544.                     cpp_refsok = FALSE;
  545.                 }
  546.             } while (ch != ';' && ch != EOF);
  547.  
  548.             /* resume echoing to "refs" */
  549.             if (!cpp_refsok)
  550.             {
  551.                 cpp_refsok = TRUE;
  552.                 cpp_echo('}');
  553.                 cpp_echo(';');
  554.             }
  555.             token = SEMICOLON;
  556.             break;
  557.  
  558.           case EOF:
  559.             token = EOF;
  560.             break;
  561.  
  562.           default:
  563.             /* is this the start of a name/keyword? */
  564.             if (isalpha(ch) || ch == '_')
  565.             {
  566.                 /* collect the whole word */
  567.                 lex_name[0] = ch;
  568.                 for (i = 1, ch = cpp_getc();
  569.                      i < BLKSIZE - 1 && (isalnum(ch) || ch == '_');
  570.                      i++, ch = cpp_getc())
  571.                 {
  572.                     lex_name[i] = ch;
  573.                 }
  574.                 lex_name[i] = '\0';
  575.                 cpp_ungetc(ch);
  576.  
  577.                 /* is it a reserved word? */
  578.                 if (!strcmp(lex_name, "typedef"))
  579.                 {
  580.                     token = TYPEDEF;
  581.                     lex_seek = -1L;
  582.                 }
  583.                 else if (!strcmp(lex_name, "static")
  584.                       || !strcmp(lex_name, "private")
  585.                       || !strcmp(lex_name, "PRIVATE"))
  586.                 {
  587.                     token = KSTATIC;
  588.                     lex_seek = -1L;
  589.                 }
  590.                 else if (!strcmp(lex_name, "extern")
  591.                       || !strcmp(lex_name, "EXTERN")
  592.                       || !strcmp(lex_name, "FORWARD"))
  593.                 {
  594.                     token = EXTERN;
  595.                     lex_seek = -1L;
  596.                 }
  597.                 else
  598.                 {
  599.                     token = NAME;
  600.                     lex_seek = file_seek;
  601.                 }
  602.             }
  603.             else /* not part of a name/keyword */
  604.             {
  605.                 token = DELETED;
  606.             }
  607.  
  608.         } /* end switch(ch) */
  609.  
  610.     } while (token == DELETED);
  611.  
  612.     return token;
  613. }
  614.  
  615. /* -------------------------------------------------------------------------- */
  616. /* This is the parser.  It locates tag candidates, and then decides whether to
  617.  * generate a tag for them.
  618.  */
  619.  
  620. /* This function generates a tag for the object in lex_name, whose tag line is
  621.  * located at a given seek offset.
  622.  */
  623. void maketag(scope, seek)
  624.     int    scope;    /* 0 if global, or KSTATIC if static */
  625.     long    seek;    /* the seek offset of the line */
  626. {
  627.     /* output the tagname and filename fields */
  628.     if (scope == EXTERN)
  629.     {
  630.         /* whoa!  we should *never* output a tag for "extern" decl */
  631.         return;
  632.     }
  633.     else if (scope == KSTATIC && !no_colons)
  634.     {
  635.         fprintf(tags, "%s:%s\t%s\t", file_name, lex_name, file_name);
  636.     }
  637.     else
  638.     {
  639.         fprintf(tags, "%s\t%s\t", lex_name, file_name);
  640.     }
  641.  
  642.     /* output the target line */
  643.     putc(backward ? '?' : '/', tags);
  644.     putc('^', tags);
  645.     file_copyline(seek, tags);
  646.     putc('$', tags);
  647.     putc(backward ? '?' : '/', tags);
  648.     putc('\n', tags);
  649. }
  650.  
  651.  
  652. /* This function parses a source file, adding any tags that it finds */
  653. void ctags(name)
  654.     char    *name;    /* the name of a source file to be checked */
  655. {
  656.     int    prev;    /* the previous token from the source file */
  657.     int    token;    /* the current token from the source file */
  658.     int    scope;    /* normally 0, but could be a TYPEDEF or KSTATIC token */
  659.     int    gotname;/* boolean: does lex_name contain a tag candidate? */
  660.     long    tagseek;/* start of line that contains lex_name */
  661.  
  662.     /* open the file */
  663.     cpp_open(name);
  664.  
  665.     /* reset */
  666.     scope = 0;
  667.     gotname = FALSE;
  668.     token = SEMICOLON;
  669.  
  670.     /* parse until the end of the file */
  671.     while (prev = token, (token = lex_gettoken()) != EOF)
  672.     {
  673.         /* scope keyword? */
  674.         if (token == TYPEDEF || token == KSTATIC || token == EXTERN)
  675.         {
  676.             scope = token;
  677.             gotname = FALSE;
  678.             continue;
  679.         }
  680.  
  681.         /* name of a possible tag candidate? */
  682.         if (token == NAME)
  683.         {
  684.             tagseek = file_seek;
  685.             gotname = TRUE;
  686.             continue;
  687.         }
  688.  
  689.         /* if NAME BODY, without ARGS, then NAME is a struct tag */
  690.         if (gotname && token == BODY && prev != ARGS)
  691.         {
  692.             gotname = FALSE;
  693.             
  694.             /* ignore if in typedef -- better name is coming soon */
  695.             if (scope == TYPEDEF)
  696.             {
  697.                 continue;
  698.             }
  699.  
  700.             /* generate a tag, if -t and maybe -s */
  701.             if (incl_types && (file_header || incl_static))
  702.             {
  703.                 maketag(file_header ? 0 : KSTATIC, tagseek);
  704.             }
  705.         }
  706.  
  707.         /* If NAME ARGS BODY, then NAME is a function */
  708.         if (gotname && prev == ARGS && token == BODY)
  709.         {
  710.             gotname = FALSE;
  711.             
  712.             /* generate a tag, maybe checking -s */
  713.             if (scope != KSTATIC || incl_static)
  714.             {
  715.                 maketag(scope, tagseek);
  716.             }
  717.         }
  718.  
  719.         /* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef.
  720.          * Note that NAME ARGS SEMICOLON is an extern function declaration,
  721.          * even if the word "extern" was left off, so we need to guard
  722.          * against that possibility.
  723.          */
  724.         if (gotname && (token == SEMICOLON || token == COMMA) && prev != ARGS)
  725.         {
  726.             gotname = FALSE;
  727.  
  728.             /* generate a tag, if -v/-t and maybe -s */
  729.             if (scope == TYPEDEF && incl_types && (file_header || incl_static)
  730.              || scope == KSTATIC && incl_vars && incl_static
  731.              || incl_vars)
  732.             {
  733.                 /* a TYPEDEF outside of a header is KSTATIC */
  734.                 if (scope == TYPEDEF && !file_header)
  735.                 {
  736.                     maketag(KSTATIC, tagseek);
  737.                 }
  738.                 else /* use whatever scope was declared */
  739.                 {
  740.                     maketag(scope, tagseek);
  741.                 }
  742.             }
  743.         }
  744.  
  745.         /* reset after a semicolon or ARGS BODY pair */
  746.         if (token == SEMICOLON || (prev == ARGS && token == BODY))
  747.         {
  748.             scope = 0;
  749.             gotname = FALSE;
  750.         }
  751.     }
  752.  
  753.     /* The source file will be automatically closed */
  754. }
  755.  
  756. /* -------------------------------------------------------------------------- */
  757.  
  758. void usage()
  759. {
  760.     fprintf(stderr, "usage: ctags [flags] filenames...\n");
  761.     fprintf(stderr, "\t-B  use ?regexp? instead of /regexp/\n");
  762.     fprintf(stderr, "\t-S  include static tags, but make them look global\n");
  763.     fprintf(stderr, "\t-s  include static tags, with colons\n");
  764.     fprintf(stderr, "\t-t  include typedefs\n");
  765.     fprintf(stderr, "\t-v  include variable declarations\n");
  766.     fprintf(stderr, "\t-r  generate a \"refs\" file, too\n");
  767.     fprintf(stderr, "\t-a  append to \"tags\", instead of overwriting\n");
  768. #ifdef DEVNULL
  769.     fprintf(stderr, "\t-T  *DON'T* generate a \"tags\" file\n");
  770. #endif
  771.     exit(2);
  772. }
  773.  
  774.  
  775.  
  776. #if AMIGA
  777. # include "amiwild.c"
  778. #endif
  779.  
  780. #if VMS
  781. # include "vmswild.c"
  782. #endif
  783.  
  784. void
  785. main(argc, argv)
  786.     int    argc;
  787.     char    **argv;
  788. {
  789.     int    i, j;
  790. #ifndef __EMX__
  791. # if MSDOS || TOS || OS2
  792.     char    **wildexpand();
  793. # endif
  794. #endif
  795.  
  796.     /* build the tables used by the ctype macros */
  797.     _ct_init((uchar *)"");
  798.  
  799. #ifdef __EMX__
  800.     _wildcard(&argc, &argv);
  801. #else
  802. # if MSDOS || TOS || OS2
  803.     argv = wildexpand(&argc, argv);
  804. # endif
  805. #endif
  806.  
  807.     /* parse the option flags */
  808.     for (i = 1; i < argc && argv[i][0] == '-'; i++)
  809.     {
  810.         for (j = 1; argv[i][j]; j++)
  811.         {
  812.             switch (argv[i][j])
  813.             {
  814.               case 'B':    backward = TRUE;        break;
  815.               case 'S':    no_colons = incl_static = TRUE;    break;
  816.               case 's':    incl_static = TRUE;        break;
  817.               case 't':    incl_types = TRUE;        break;
  818.               case 'v':    incl_vars = TRUE;        break;
  819.               case 'r':    make_refs = TRUE;        break;
  820.               case 'a':    append_files = TRUE;        break;
  821. #ifdef DEVNULL
  822.               case 'T':    dont_make_tags = TRUE;        break;
  823. #endif
  824.               default:    usage();
  825.             }
  826.         }
  827.     }
  828.  
  829.     /* There should always be at least one source file named in args */
  830.     if (i == argc)
  831.     {
  832.         usage();
  833.     }
  834.  
  835.     /* open the "tags" and maybe "refs" files */
  836. #ifdef DEVNULL
  837.     if (dont_make_tags)
  838.     {
  839.         tags = fopen(DEVNULL, "w");
  840.         if (!tags)
  841.         {
  842.             perror(DEVNULL);
  843.             exit(3);
  844.         }
  845.     }
  846.     else
  847. #endif
  848.     tags = fopen(TAGS, append_files ? "a" : "w");
  849.     if (!tags)
  850.     {
  851.         perror(TAGS);
  852.         exit(3);
  853.     }
  854.     if (make_refs)
  855.     {
  856.         refs = fopen(REFS, append_files ? "a" : "w");
  857.         if (!refs)
  858.         {
  859.             perror(REFS);
  860.             exit(4);
  861.         }
  862.     }
  863.  
  864.     /* parse each source file */
  865.     for (; i < argc; i++)
  866.     {
  867.         char *p = argv[i] - 1;
  868.  
  869.         /* reconvert any converted spaces */
  870.         while (*++p)
  871.         {
  872.             if ((unsigned char)(*p) == SPACEHOLDER)
  873.             {
  874.                 *p = ' ';
  875.             }
  876.         }
  877.         ctags(argv[i]);
  878.     }
  879.  
  880.     /* close "tags" and maybe "refs" */
  881.     fclose(tags);
  882.     if (make_refs)
  883.     {
  884.         fclose(refs);
  885.     }
  886.  
  887. #ifdef SORT
  888.         /* This is a hack which will sort the tags list.   It should
  889.          * on UNIX and OS-9.  You may have trouble with csh.   Note
  890.          * that the tags list only has to be sorted if you intend to
  891.          * use it with the real vi;  elvis permits unsorted tags.
  892.          */
  893. # if OSK
  894.         system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
  895. # else    
  896.         system("sort tags >_tags$$; mv _tags$$ tags");
  897. # endif
  898. #endif
  899.  
  900.     exit(0);
  901.     /*NOTREACHED*/
  902. }
  903.  
  904. #if MSDOS || TOS || OS2
  905. # define WILDCARD_NO_MAIN
  906. # include "wildcard.c"
  907. #endif
  908.